{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入椭圆曲线算法\n",
    "from ecdsa import SigningKey, SECP256k1, VerifyingKey, BadSignatureError\n",
    "import binascii\n",
    "import base64\n",
    "from hashlib import sha256\n",
    "\n",
    "\n",
    "class Wallet:\n",
    "    \"\"\"\n",
    "        钱包\n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        \"\"\"\n",
    "            钱包初始化时基于椭圆曲线生成一个唯一的秘钥对，代表区块链上一个唯一的账户\n",
    "        \"\"\"\n",
    "        self._private_key = SigningKey.generate(curve=SECP256k1)\n",
    "        self._public_key = self._private_key.get_verifying_key()\n",
    "\n",
    "    @property\n",
    "    def address(self):\n",
    "        \"\"\"\n",
    "            这里通过公钥生成地址\n",
    "        \"\"\"\n",
    "        h = sha256(self._public_key.to_pem())\n",
    "        return base64.b64encode(h.digest())\n",
    "\n",
    "    @property\n",
    "    def pubkey(self):\n",
    "        \"\"\"\n",
    "            返回公钥字符串\n",
    "        \"\"\"\n",
    "        return self._public_key.to_pem()\n",
    "\n",
    "    def sign(self, message):\n",
    "        \"\"\"\n",
    "            生成数字签名\n",
    "        \"\"\"\n",
    "        h = sha256(message.encode('utf8'))\n",
    "        return binascii.hexlify(self._private_key.sign(h.digest()))\n",
    "\n",
    "    \n",
    "def verify_sign(pubkey, message, signature):\n",
    "    \"\"\"\n",
    "        验证签名\n",
    "    \"\"\"\n",
    "    verifier = VerifyingKey.from_pem(pubkey)\n",
    "    h = sha256(message.encode('utf8'))\n",
    "    return verifier.verify(binascii.unhexlify(signature), h.digest())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 新建一个钱包\n",
    "\n",
    "w = Wallet()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "b'dKrpZUamm1B2avx22I/OOEkUg+wsnrsRu7qVYTn5wuI='"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 打印钱包地址\n",
    "\n",
    "w.address"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "b'-----BEGIN PUBLIC KEY-----\\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE9bZoTMnsq/14DkQA/yEQG4EsvwYUhlRl\\nYoQ+yj1To97nOsMuwPkBa8zQVGpBCF/oJs0X4zCqBk41bCWeOYgFug==\\n-----END PUBLIC KEY-----\\n'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 打印钱包公钥\n",
    "\n",
    "w.pubkey"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 测试数据\n",
    "\n",
    "data = \"交易数据\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'00a40310e47fd032662f787b8e201d8edbb3ec031aabea3f287b494f9b2b21c52ac1144e187ac54eea4264e54153ef6778cac7a15e7660bcb5d0750c27d18a50'\n"
     ]
    }
   ],
   "source": [
    "# 生成签名\n",
    "\n",
    "sig = w.sign(data)\n",
    "\n",
    "# 打印签名\n",
    "print(sig)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 验证签名\n",
    "\n",
    "verify_sign(w.pubkey, data, sig)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "\n",
    "class Transaction:\n",
    "    \"\"\"\n",
    "    交易的结构\n",
    "    \"\"\"\n",
    "    def __init__(self, sender, recipient, amount):\n",
    "        \"\"\"\n",
    "            初始化交易，设置交易的发送方、接收方和交易数量\n",
    "        \"\"\"\n",
    "        if isinstance(sender, bytes):\n",
    "            sender = sender.decode('utf-8')\n",
    "        self.sender = sender            # 发送方\n",
    "        if isinstance(recipient, bytes):\n",
    "            recipient = recipient.decode('utf-8')\n",
    "        self.recipient = recipient      # 接收方\n",
    "        self.amount = amount            # 交易数量\n",
    "        \n",
    "    def set_sign(self, signature, pubkey):\n",
    "        \"\"\"\n",
    "            为了便于验证这个交易的可靠性，需要发送方输入他的公钥和签名\n",
    "        \"\"\"\n",
    "        self.signature = signature      # 签名\n",
    "        self.pubkey = pubkey            # 发送方公钥\n",
    "        \n",
    "    def __repr__(self):\n",
    "        \"\"\"\n",
    "            交易大致可分为两种，一是挖矿所得，而是转账交易\n",
    "            挖矿所得无发送方，以此进行区分显示不同内容\n",
    "        \"\"\"\n",
    "        if self.sender:\n",
    "            s = \"从 %s 转至 %s %d个加密货币\" % (self.sender, self.recipient, self.amount)\n",
    "        else:\n",
    "            s = \"%s 挖矿获取%d个加密货币\" % (self.recipient, self.amount)\n",
    "        return s\n",
    "\n",
    "\n",
    "class TransactionEncoder(json.JSONEncoder):\n",
    "    \"\"\"\n",
    "    定义Json的编码类，用来序列化Transaction\n",
    "    \"\"\"\n",
    "    def default(self, obj):\n",
    "        if isinstance(obj, Transaction):\n",
    "            return obj.__dict__\n",
    "        else:\n",
    "            return json.JSONEncoder.default(self, obj)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "import hashlib\n",
    "from datetime import datetime\n",
    "\n",
    "\n",
    "class Block:\n",
    "    \"\"\"\n",
    "        区块结构\n",
    "            prev_hash:      父区块哈希值\n",
    "            transactions:           交易对\n",
    "            timestamp:      区块创建时间\n",
    "            hash:           区块哈希值\n",
    "            Nonce:        随机数\n",
    "    \"\"\"\n",
    "    def __init__(self, transactions, prev_hash):\n",
    "        # 将传入的父哈希值和数据保存到类变量中\n",
    "        self.prev_hash = prev_hash    \n",
    "        self.transactions = transactions\n",
    "        # 获取当前时间\n",
    "        self.timestamp = datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")\n",
    "        \n",
    "        # 设置Nonce和哈希的初始值为None\n",
    "        self.nonce = None\n",
    "        self.hash = None\n",
    "        \n",
    "    def __repr__(self):\n",
    "        return \"区块内容：%s\\n哈希值: %s\" % (json.dumps(self.transactions), self.hash)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "class ProofOfWork:\n",
    "    \"\"\"\n",
    "        工作量证明\n",
    "    \"\"\"\n",
    "    def __init__(self, block, miner, difficult=5):\n",
    "        self.block = block\n",
    "        \n",
    "        # 定义工作量难度，默认为5，表示有效的哈希值以5个“0”开头\n",
    "        self.difficulty = difficult\n",
    "\n",
    "        self.miner = miner\n",
    "        # 添加挖矿奖励\n",
    "        self.reward_amount = 1\n",
    "\n",
    "    def mine(self):\n",
    "        \"\"\"\n",
    "            挖矿函数\n",
    "        \"\"\"\n",
    "        i = 0\n",
    "        prefix = '0' * self.difficulty\n",
    "        \n",
    "        \n",
    "        # 添加奖励\n",
    "        t = Transaction(\n",
    "                sender=\"\",\n",
    "                recipient=self.miner.address,\n",
    "                amount=self.reward_amount,\n",
    "            )\n",
    "        sig = self.miner.sign(json.dumps(t, cls=TransactionEncoder))\n",
    "        t.set_sign(sig, self.miner.pubkey)\n",
    "        self.block.transactions.append(t)\n",
    "\n",
    "        while True:\n",
    "            message = hashlib.sha256()\n",
    "            message.update(str(self.block.prev_hash).encode('utf-8'))\n",
    "            # 更新区块中的交易数据\n",
    "            # message.update(str(self.block.data).encode('utf-8'))\n",
    "            message.update(str(self.block.transactions).encode('utf-8'))\n",
    "            message.update(str(self.block.timestamp).encode('utf-8'))\n",
    "            message.update(str(i).encode(\"utf-8\"))\n",
    "            digest = message.hexdigest()\n",
    "            if digest.startswith(prefix):\n",
    "                self.block.nonce = i\n",
    "                self.block.hash = digest\n",
    "                return self.block\n",
    "            i += 1\n",
    "\n",
    "    def validate(self):\n",
    "        \"\"\"\n",
    "            验证有效性\n",
    "        \"\"\"\n",
    "        message = hashlib.sha256()\n",
    "        message.update(str(self.block.prev_hash).encode('utf-8'))\n",
    "        # 更新区块中的交易数据\n",
    "        # message.update(str(self.block.data).encode('utf-8'))\n",
    "        message.update(json.dumps(self.block.transactions).encode('utf-8'))\n",
    "        message.update(str(self.block.timestamp).encode('utf-8'))\n",
    "        message.update(str(self.block.nonce).encode('utf-8'))\n",
    "        digest = message.hexdigest()\n",
    "\n",
    "        prefix = '0' * self.difficulty\n",
    "        return digest.startswith(prefix)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "class BlockChain:\n",
    "    \"\"\"\n",
    "        区块链结构体\n",
    "            blocks:        包含的区块列表\n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        self.blocks = []\n",
    "\n",
    "    def add_block(self, block):\n",
    "        \"\"\"\n",
    "        添加区块\n",
    "        \"\"\"\n",
    "        self.blocks.append(block)\n",
    "    \n",
    "    def print_list(self):\n",
    "        print(\"区块链包含区块个数: %d\\n\" % len(self.blocks))\n",
    "        for block in self.blocks:\n",
    "            print(\"上个区块哈希：%s\" % block.prev_hash)\n",
    "            print(\"区块内容：%s\" % block.transactions)\n",
    "            print(\"区块哈希：%s\" % block.hash)\n",
    "            print(\"\\n\") "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_balance(user):\n",
    "    balance = 0\n",
    "    for block in blockchain.blocks:\n",
    "        for t in block.transactions:\n",
    "            if t.sender == user.address.decode():\n",
    "                balance -= t.amount\n",
    "            elif t.recipient == user.address.decode():\n",
    "                balance += t.amount\n",
    "    return balance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "alice: 0个加密货币\n",
      "tom: 0个加密货币\n",
      "bob: 0个加密货币\n"
     ]
    }
   ],
   "source": [
    "# 初始化区块链\n",
    "blockchain = BlockChain()\n",
    "\n",
    "# 创建三个钱包，一个属于alice，一个属于tom，剩下一个属于bob\n",
    "alice = Wallet()\n",
    "tom = Wallet()\n",
    "bob = Wallet()\n",
    "\n",
    "# 打印当前钱包情况\n",
    "print(\"alice: %d个加密货币\" % (get_balance(alice)))\n",
    "print(\"tom: %d个加密货币\" % (get_balance(tom)))\n",
    "print(\"bob: %d个加密货币\" % (get_balance(bob)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# alice生成创世区块，并添加到区块链中\n",
    "\n",
    "new_block1 = Block(transactions=[], prev_hash=\"\")\n",
    "w1 = ProofOfWork(new_block1, alice)\n",
    "genesis_block = w1.mine()\n",
    "blockchain.add_block(genesis_block)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "alice: 1个加密货币\n"
     ]
    }
   ],
   "source": [
    "# 显示alice当前余额\n",
    "\n",
    "print(\"alice: %d个加密货币\" % (get_balance(alice)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# alice 转账给 tom 0.3个加密货币\n",
    "transactions = []\n",
    "new_transaction = Transaction(\n",
    "    sender=alice.address,\n",
    "    recipient=tom.address,\n",
    "    amount=0.3\n",
    ")\n",
    "sig = tom.sign(str(new_transaction))\n",
    "new_transaction.set_sign(sig, tom.pubkey)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "验证交易成功\n",
      "生成新的区块...\n",
      "将新区块添加到区块链中\n"
     ]
    }
   ],
   "source": [
    "# bob 在网络上接收到这笔交易，进行验证没问题后生成一个新的区块并添加到区块链中\n",
    "\n",
    "if verify_sign(new_transaction.pubkey, \n",
    "                  str(new_transaction),\n",
    "                   new_transaction.signature):\n",
    "    \n",
    "    # 验证交易签名没问题，生成一个新的区块\n",
    "    print(\"验证交易成功\")\n",
    "    new_block2 = Block(transactions=[new_transaction], prev_hash=\"\")\n",
    "    print(\"生成新的区块...\")\n",
    "    w2 = ProofOfWork(new_block2, bob)\n",
    "    block = w2.mine()\n",
    "    print(\"将新区块添加到区块链中\")\n",
    "    blockchain.add_block(block)\n",
    "else:\n",
    "    print(\"交易验证失败！\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "alice: 0.7个加密货币\n",
      "tom: 0.3个加密货币\n",
      "bob: 1个加密货币\n"
     ]
    }
   ],
   "source": [
    "# 打印当前钱包情况\n",
    "print(\"alice: %.1f个加密货币\" % (get_balance(alice)))\n",
    "print(\"tom: %.1f个加密货币\" % (get_balance(tom)))\n",
    "print(\"bob: %d个加密货币\" % (get_balance(bob)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
